import logging
from evoque.domain import Domain
from os.path import abspath
from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.internet.ssl import DefaultOpenSSLContextFactory
from twisted.python import context
from twisted.web.http import INTERNAL_SERVER_ERROR
from twisted.web.resource import Resource
from twisted.web.server import NOT_DONE_YET
from twisted.web.server import Site
from twisted.web.static import File

from littlesite import config
from littlesite.db import initConnectionPool


class LittleSite:
    def __init__(self, resourceDir):
        self.reactor = None
        self.root = File(resourceDir)
        self.domain = Domain(abspath('resources'))

    @inlineCallbacks
    def _connectDatabase(self):
        yield initConnectionPool()

    def _startHttpServer(self):
        site = Site(self.root)
        self.port = self.reactor.listenTCP(config.server_port, site,
                                           interface=config.server_host)
        context_factory = DefaultOpenSSLContextFactory(config.ssl_key,
                                                       config.ssl_cert)
        if config.use_ssl:
            self.ssl_port = self.reactor.listenSSL(config.server_ssl_port, site,
                                                   context_factory,
                                                   interface=config.server_host)

    def _startMonitorServer(self):
        self_ = self

        class MonitorResource(Resource):
            isLeaf = True

            def render_GET(self, request):
                self.stop(request)
                return NOT_DONE_YET

            @inlineCallbacks
            def stop(self, request):
                try:
                    logging.fatal('Stopping HTTP server.')
                    yield self_.port.loseConnection()
                    if hasattr(self_, 'ssl_port'):
                        yield self_.ssl_port.loseConnection()
                except:
                    logging.fatal('Failed to stop HTTP server.', exc_info=True)
                    request.setResponseCode(INTERNAL_SERVER_ERROR)
                else:
                    self_.reactor.callFromThread(self.stopReactor)
                finally:
                    request.finish()

            def stopReactor(self):
                logging.fatal('Exit now.')
                self_.reactor.stop()


        site = Site(MonitorResource())
        self.reactor.listenTCP(4444, site, interface='127.0.0.1')

    @inlineCallbacks
    def _startServer(self):
        try:
            yield self._connectDatabase()
        except:
            self.reactor.callFromThread(self.reactor.stop)
            logging.fatal('Failed to connect to DB.', exc_info=True)
            returnValue(None)

        try:
            ctx = {'domain': self.domain}
            yield context.call(ctx, self._startHttpServer)
        except:
            self.reactor.callFromThread(self.reactor.stop)
            logging.fatal('Failed to start HTTP server.', exc_info=True)
            returnValue(None)

        try:
            self._startMonitorServer()
        except:
            logging.fatal('Failed to start monitor server.', exc_info=True)

    def putChild(self, path, resource):
        self.root.putChild(path, resource)

    def run(self, reactor=None):
        if reactor is None:
            from twisted.internet import reactor
        self.reactor = reactor
        reactor.callFromThread(self._startServer)
        reactor.run()
